////////////////////////////////////////////////////////////////////////////////

#ifndef _PHYSICSENGINE_H_
#include "physicsengine.h"
#endif

#ifndef _SGAMEOBJECT_H_
#include "sgameobject.h"
#endif

#ifndef _SBULLET_H_
#include "sbullet.h"
#endif

#ifndef _SPLAYER_H_
#include "splayer.h"
#endif

#ifndef _COMMONTYPES_H_
#include "commontypes.h"
#endif

#ifndef _MAPONSERVER_H_
#include "maponserver.h"
#endif

////////////////////////////////////////////////////////////////////////////////


PhysicsEngine::PhysicsEngine()
{
    b2Vec2 gravity(0.0f, 0.0f);
    bool doSleep = true;

    m_World.reset( new b2World(gravity, doSleep) );
    m_World->SetContactListener(&m_ContactListener);
}


////////////////////////////////////////////////////////////////////////////////


PhysicsEngine::~PhysicsEngine()
{
}


////////////////////////////////////////////////////////////////////////////////


void PhysicsEngine::DestroyObject( SGameObject& _object )
{
    b2Body* physicalObject = static_cast<b2Body*>( _object.GetPhysicalObject() );
    assert(physicalObject);

    m_World->DestroyBody(physicalObject);
}


////////////////////////////////////////////////////////////////////////////////


void PhysicsEngine::Update( size_t _dt )
{
    m_World->Step( (float)_dt/1000.0f, 10, 5 );
    b2Body* body = m_World->GetBodyList();

    // Update game objects
    while( body )
    {
        if ( body->GetUserData() )
        {
            SGameObject* gameObj = static_cast<SGameObject*>( body->GetUserData() );
            gameObj->SetPosition( Point(body->GetPosition().x, body->GetPosition().y) );
        }
        body = body->GetNext();
    }
}


////////////////////////////////////////////////////////////////////////////////


void PhysicsEngine::CreatePlayer( SPlayer& _player )
{
    b2CircleShape shape;
    shape.m_radius = 1.0f;

    b2FixtureDef fixture;
    fixture.shape = &shape;
    fixture.density = 32.0f;
    fixture.restitution = 0.5f;

    CreateObject(fixture, _player);

    b2Body* physicalObject = static_cast<b2Body*>( _player.GetPhysicalObject() );
    assert(physicalObject);
    physicalObject->SetLinearDamping(0.5f);
    physicalObject->SetAngularDamping(0.1f);
}


////////////////////////////////////////////////////////////////////////////////


void PhysicsEngine::CreateBullet( SBullet& _bullet, const Vector& _impulse )
{
    b2CircleShape shape;
    shape.m_radius = 0.4f;
    
    b2FixtureDef fixture;
    fixture.shape = &shape;
    fixture.density = 40.0f;
    fixture.restitution = 0.9f;

    CreateObject(fixture, _bullet);

    b2Body* physicalObject = static_cast<b2Body*>( _bullet.GetPhysicalObject() );
    assert(physicalObject);

    physicalObject->SetBullet(true);
    b2Vec2 impulse(_impulse.x, _impulse.y);
    physicalObject->ApplyLinearImpulse( impulse, physicalObject->GetPosition() );
}


////////////////////////////////////////////////////////////////////////////////


void PhysicsEngine::CreateObject( b2FixtureDef& _fixture, SGameObject& _object, bool _isStatic )
{
    b2BodyDef bodyDef;
    bodyDef.position.Set(_object.GetPosition().x, _object.GetPosition().y);
    bodyDef.active = true;
    bodyDef.fixedRotation = true;
    bodyDef.type = _isStatic ? b2_staticBody : b2_dynamicBody;

    b2Body* body = m_World->CreateBody(&bodyDef);
    
    body->CreateFixture(&_fixture);

    _object.SetPhysicalObject( static_cast<void*>(body) );
}


////////////////////////////////////////////////////////////////////////////////


void PhysicsEngine::ApplyImpulse( SGameObject& _object,
                                  const Vector& _impulse )
{
    b2Body* physicalObject = static_cast<b2Body*>( _object.GetPhysicalObject() );
    assert(physicalObject);

    physicalObject->ApplyLinearImpulse( b2Vec2(_impulse.x, _impulse.y),
                                        physicalObject->GetPosition() );
}


////////////////////////////////////////////////////////////////////////////////


void PhysicsEngine::SetUserData( SGameObject& _userData )
{
    b2Body* physicalObject = static_cast<b2Body*>( _userData.GetPhysicalObject() );
    assert(physicalObject);

    physicalObject->SetUserData(&_userData);
}


////////////////////////////////////////////////////////////////////////////////


void PhysicsEngine::SetMap( const MapOnServer& _map )
{
    // Now b2World does not require AABB size
    //b2Vec2 gravity(0.0f, 0.0f);
    //bool doSleep = true;

    //m_World.reset( new b2World(gravity, doSleep) );
}


////////////////////////////////////////////////////////////////////////////////
